// RelationshipMap.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

using Supremacy.Entities;
using Supremacy.Game;
using Supremacy.IO.Serialization;

namespace Supremacy.Diplomacy
{
    [Serializable]
    public sealed class RelationshipMap : IOwnedDataSerializableAndRecreatable, IEnumerable<Relationship>
    {
        private Dictionary<int, Relationship> _map;

        private RelationshipMap()
        {
            Initialize();
        }

        public RelationshipMap(IEnumerable<Relationship> relationships) : this()
        {
            if (relationships == null)
                throw new ArgumentNullException("relationships");
            foreach (Relationship relationship in relationships)
            {
                this[relationship.OwnerCivID, relationship.OtherCivID] = relationship;
            }
        }

        private void Initialize()
        {
            _map = new Dictionary<int, Relationship>();
        }

        private static int GetKey(int ownerCivId, int otherCivId)
        {
            return ((ownerCivId << 16) | otherCivId);
        }

        public Relationship this[Civilization ownerCiv, Civilization otherCiv]
        {
            get
            {
                if (ownerCiv == null)
                    throw new ArgumentNullException("ownerCiv");
                if (otherCiv == null)
                    throw new ArgumentNullException("otherCiv");
                return this[ownerCiv.CivID, otherCiv.CivID];
            }
            private set
            {
                if (ownerCiv == null)
                    throw new ArgumentNullException("ownerCiv");
                if (otherCiv == null)
                    throw new ArgumentNullException("otherCiv");
                this[ownerCiv.CivID, otherCiv.CivID] = value;
            }
        }

        public Relationship this[int ownerCivId, int otherCivId]
        {
            get
            {
                Relationship result;
                if (_map.TryGetValue(GetKey(ownerCivId, otherCivId), out result))
                    return result;
                return null;
            }
            private set
            {   
                if (value == null)
                {
                    _map.Remove(GetKey(ownerCivId, otherCivId));
                }
                else
                {
                    _map[GetKey(ownerCivId, otherCivId)] = value;
                }
            }
        }

        public IEnumerable<Relationship> GetRelationshipsForCiv(Civilization civ)
        {
            foreach (Civilization otherCiv in GameContext.Current.Civilizations)
            {
                if (otherCiv != civ)
                {
                    Relationship relationship = this[civ, otherCiv];
                    if (relationship != null)
                        yield return relationship;
                }
            }
        }

        public static RelationshipMap Create(GameContext game)
        {
            if (game == null)
                throw new ArgumentNullException("game");
            
            RelationshipMap map = new RelationshipMap();

            game.Relationships = map;

            foreach (Civilization civ1 in game.Civilizations)
            {
                foreach (Civilization civ2 in game.Civilizations)
                {
                    if (civ1 != civ2)
                    {
                        Relationship relationship = new Relationship(civ1, civ2);
                        map[civ1, civ2] = relationship;
                        game.DiplomacyStatus[civ1, civ2] = relationship.CurrentState;
                    }
                }
            }

            return map;
        }

        #region IOwnedDataSerializable Members
        public void SerializeOwnedData(SerializationWriter writer, object context)
        {
            var relationships = _map.Values.ToList();
            writer.Write(relationships.Count);
            foreach (Relationship relationship in relationships)
            {
                writer.WriteObject(relationship);
            }
        }

        public void DeserializeOwnedData(SerializationReader reader, object context)
        {
            Initialize();

            int count = reader.ReadInt32();
            while (_map.Count < count)
            {
                Relationship relationship = reader.Read<Relationship>();
                this[relationship.OwnerCivID, relationship.OtherCivID] = relationship;
            }
        }
        #endregion

        IEnumerator<Relationship> IEnumerable<Relationship>.GetEnumerator()
        {
            return _map.Values.GetEnumerator();
        }

        public IEnumerator GetEnumerator()
        {
            return ((IEnumerable<Relationship>)this).GetEnumerator();
        }
    }
}
